package 方法句柄;

import static org.junit.Assert.assertEquals;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.List;


方法句柄的调用可分为两种，一是需要严格匹配参数类型的 invokeExact。它有多严格呢？假设一个方法句柄将接收一个 Object 类型的参数，如果你直接传入 String 作为实际
参数，那么方法句柄的调用会在运行时抛出方法类型不匹配的异常。正确的调用方式是将该 String 显式转化为 Object 类型。

invokeExact 位于 MethodHandle 文件中

public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
方法句柄 API 有一个特殊的注解类 @PolymorphicSignature。在碰到被它注解的方法调用时，Java 编译器会根据所传入参数的声明类型来生成方法描述符，而不是采用目标方法
所声明的描述符。

public class Cobra { 
    public void read(Object object){ 
    }
}
public class RaceTest { 
    public static void main(String[] args) throws Throwable { 
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      Cobra cobra = new Cobra();    
      MethodHandle methodHandle = lookup.findVirtual(Cobra.class, "read", MethodType.methodType(void.class, Object.class));
      methodHandle.invokeExact(cobra,"123");    
      methodHandle.invokeExact(cobra,(Object) "456");
      }
    }
//编译RaceTest，查看其字节码
	13: ldc           #3                  // class com/msdn/java/hotspot/invokedynamic/Cobra        
	15: ldc           #5                  // String read        
	17: getstatic     #6                  // Field java/lang/Void.TYPE:Ljava/lang/Class;        
	20: ldc           #7                  // class java/lang/Object        
	22: invokestatic  #8                  // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;        
	25: invokevirtual #9                  // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;        
	28: astore_3        
	29: aload_3        
	30: ldc           #10                 // String 123        
	32: invokevirtual #11                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;)V        
	35: aload_3        
	36: ldc           #12                 // String 456        
	38: invokevirtual #13                 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/Object;)V        
	41: return


//用法示例
//以下是一些用法示例：
public class invoke使用 {

	public static void main(String[] args) throws Throwable{
		Object x, y;
		String s;
		int i;
		MethodType mt;
		MethodHandle mh;
		MethodHandles.Lookup lookup = MethodHandles.lookup();
		
		mt = MethodType.methodType(String.class, char.class, char.class);// mt is (char,char)String
		mh = lookup.findVirtual(String.class, "replace", mt);
		s = (String) mh.invokeExact("daddy", 'd', 'n');// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
		assertEquals(s, "nanny");
		
		s = (String) mh.invokeWithArguments("sappy", 'p', 'v');// weakly typed invocation (using MHs.invoke)
		assertEquals(s, "savvy");

		
		mt = MethodType.methodType(java.util.List.class, Object[].class);// mt is (Object[])List
		mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
		assert (mh.isVarargsCollector());
		x = mh.invoke("one", "two");// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
		System.out.println(x.toString());//[one, two]
		assertEquals(x, java.util.Arrays.asList("one", "two"));
		
		mt = MethodType.genericMethodType(3);// mt is (Object,Object,Object)Object
		mh = mh.asType(mt);
		x = mh.invokeExact((Object) 1, (Object) 2, (Object) 3);// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
		System.out.println(x.toString());//[1, 2, 3]
		assertEquals(x, java.util.Arrays.asList(1, 2, 3));
		
		mt = MethodType.methodType(int.class);// mt is ()int
		mh = lookup.findVirtual(java.util.List.class, "size", mt);
		List list=Arrays.asList(1,2,3);
		i=(int)mh.invokeExact(list);
		//i = (int) mh.invokeExact(java.util.Arrays.asList(1, 2, 3));// invokeExact(Ljava/util/List;)I
		assert (i == 3);
		
		mt = MethodType.methodType(void.class, String.class);//mt is (String)V
		mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
		mh.invokeExact(System.out, "Hello, world.");// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
	}

}



/*
 * 上述对invokeExact或plain invoke每个调用invoke生成一个invoke指令，其中符号类型描述符在以下注释中指示。
 * 在这些示例中，假设辅助方法assertEquals是对 其参数调用Objects.equals的方法，并断言结果为true。 例外
 * 方法invokeExact和invoke被声明为抛出Throwable ，也就是说方法句柄可以抛出的内容没有静态限制。
 * 由于JVM不区分已检查和未检查的异常（当然除了它们的类之外），因此将检查的异常归因于方法句柄调用对字节码形状没有特别的影响。
 * 但是在Java源代码中，执行方法句柄调用的方法必须显式抛出Throwable
 * ，否则必须在本地捕获所有throwable，重新抛出那些在上下文中合法的东西，并包装那些非法的抛出。 签名多态性 invokeExact和plain
 * invoke的异常编译和链接行为由术语签名多态性引用。 如Java语言规范中所定义的，签名多态方法是可以使用各种呼叫签名和返回类型操作的方法。
 * 在源代码中，无论请求的符号类型描述符如何，都将编译对签名多态方法的调用。
 * 像往常一样，Java编译器使用给定的符号类型描述符针对命名方法发出invokevirtual指令。
 * 不寻常的部分是符号类型描述符是从实际的参数和返回类型派生的，而不是从方法声明派生的。
 * 
 * 当JVM处理包含签名多态调用的字节码时，无论其符号类型描述符如何，它都将成功链接任何此类调用。
 * （为了保持类型安全性，JVM将使用适当的动态类型检查来保护此类调用，如其他地方所述。）
 * 
 * 字节码生成器（包括编译器后端）需要为这些方法发出未转换的符号类型描述符。 确定符号链接的工具需要接受这种未转换的描述符，而不报告链接错误。
 * 
 * 方法句柄和Core Reflection API之间的互操作 使用Lookup API中的工厂方法，可以将Core Reflection
 * API对象表示的任何类成员转换为行为等效的方法句柄。 例如，可以使用Lookup.unreflect将反射方法转换为方法句柄。
 * 生成的方法句柄通常提供对底层类成员的更直接和有效的访问。 作为特殊情况，当Core Reflection
 * API用于查看invokeExact的签名多态方法invokeExact或plain invoke时，它们看起来像普通的非多态方法。
 * 如Class.getDeclaredMethod所示 ，它们的反射外观不受其在此API中的特殊状态的影响。 例如，
 * Method.getModifiers将准确报告任何类似声明的方法所需的那些修饰符位，包括native和varargs位。
 * 
 * 与任何反射方法一样，这些方法（反映时）可以通过java.lang.reflect.Method.invoke调用。
 * 但是，这种反射调用不会导致方法句柄调用。
 * 这样的调用，如果传递了必需的参数（类型为Object[]的单个参数），将忽略该参数并抛出UnsupportedOperationException 。
 * 
 * 由于invokevirtual指令可以在任何符号类型描述符下本机调用方法句柄，因此该反射视图与通过字节码的这些方法的正常表示冲突。
 * 因此，当Class.getDeclaredMethod反思地查看时，这两种本机方法可以仅被视为占位符。
 * 
 * 要获取特定类型描述符的调用方法，请使用MethodHandles.exactInvoker或MethodHandles.invoker 。
 * 对于任何指定的类型描述符， Lookup.findVirtual API还能够返回一个方法句柄来调用invokeExact或plain invoke 。
 * 
 * 方法句柄和Java泛型之间的互操作 可以在使用Java泛型类型声明的方法，构造函数或字段上获取方法句柄。 与Core Reflection
 * API一样，方法句柄的类型将从源级别类型的擦除构造。 调用方法句柄时，其参数类型或返回值强制类型可以是泛型类型或类型实例。
 * 如果发生这种情况，编译器在构造invokevirtual指令的符号类型描述符时将通过其擦除替换这些类型。
 * 方法句柄不代表Java参数化（通用）类型的类函数类型，因为类函数类型和参数化Java类型之间存在三种不匹配。
 * 
 * 方法类型适用于所有可能的arities，从无参数到最多maximum number的允许参数。 泛型不是变量，因此不能代表这一点。
 * 方法类型可以指定基本类型的参数，Java泛型类型不能超出范围。 方法句柄（组合器）上的高阶函数通常在各种函数类型中是通用的，包括多个元素的函数类型。
 * 用Java类型参数表示这种通用性是不可能的。 Arity limits JVM强加任何类型的所有方法和构造函数，绝对限制为255个堆栈参数。
 * 在某些情况下，此限制似乎更具限制性： long或double参数计算（出于arity限制的目的）作为两个参数槽。
 * 非静态方法为调用该方法的对象使用额外的参数。 构造函数为正在构造的对象使用额外的参数。
 * 由于方法句柄invoke方法（或其他签名多态方法）是非虚拟的，因此除了任何非虚拟接收器对象之外，它还会为方法句柄本身消耗额外的参数。
 * 这些限制意味着无法创建某些方法句柄，仅仅因为堆栈参数的JVM限制。 例如，如果静态JVM方法只接受255个参数，则无法为其创建方法句柄。
 * 尝试使用不可能的方法类型创建方 法句柄会导致IllegalArgumentException 。
 * 特别是，方法句柄的类型不能具有精确最大值为255的arity。
 */